package sweetsaremeat.beehive.Model;

import android.util.FloatMath;


public class Node
{
	/** Enumerators */
	//the sides of a node
	public static enum Side 
	{
	    TopRight, 
	    MidRight, 
	    BotRight, 
	    BotLeft,
	    MidLeft, 
	    TopLeft
	}
	
	/** Static Variables */
	public static int UniqueId = 0;
	public static float xOffset = FloatMath.sqrt(3);
	public static float yOffset = 1.5f;
	
	/** Variables */
	//the neightbors of this node
	public Node[] m_neighbors = new Node[Side.values().length];
	public boolean[] m_connectedTo = new boolean[m_neighbors.length];
	
	public final int m_id;
	
	public Vector2 m_pos;
	
	public boolean m_visible;
	
	public boolean m_isPath;
	
	public boolean m_tag = false;//testing purposes
	
    /** Constructor */
    public Node(float a_x, float a_y) 
    {
    	m_id = UniqueId++;
    	
    	m_pos = new Vector2(a_x, a_y);
    	
    	m_visible = false;
    	
    	m_isPath = false;
	}
    
    public void CreateNeighbor(Side a_side)
    {
    	float f_xOffset = 0, f_yOffset = 0;
    	//TODO use GetNeighborPos() instead
    	
    	//calculate the position
    	switch(a_side)
    	{
    	case TopRight:
			//upper-right
    		f_xOffset = xOffset * 0.5f;
    		f_yOffset = -yOffset;
			break;
		case MidRight:
			//mid-right
			f_xOffset = xOffset;
			break;
		case BotRight:
			//low-right
			f_xOffset = xOffset * 0.5f;
			f_yOffset = yOffset;
			break;
		case BotLeft:
			//low-left
			f_xOffset = -xOffset * 0.5f;
			f_yOffset = yOffset;
			break;
		case MidLeft:
			//mid-left
			f_xOffset = -xOffset;
			break;
		case TopLeft:
			//upper-left
			f_xOffset = (float)(-xOffset * 0.5);
			f_yOffset = -yOffset;
			break;
    	}
    	
    	
    	//create the new node
    	Node f_node = new Node(m_pos.x + f_xOffset, m_pos.y + f_yOffset);
    	
    	//connect it as your neighbor at the chosen side
    	ConnectNeighbor(f_node, a_side.ordinal(), null);
    }
    
    public void CreateNeighbor(int a_side)
    {
    	//calculate the position
    	Vector2 f_neighborPos = GetNeighborPos(a_side);
    	
    	//create the new node
    	Node f_node = new Node(f_neighborPos.x, f_neighborPos.y);
    	
    	//connect it as your neighbor at the chosen side
    	ConnectNeighbor(f_node, a_side, null);
    }
    
    
    //this should be a recursive function
    public void ConnectNeighbor(Node a_node, int a_side, Node a_previousNode)
    {
    	//check if the node this is connecting to is NULL
    	if(a_node != null)
    	{
	    	//calc the opposite side
	    	int f_tempSide = a_side - 3; //-3 because that's the opposite side
	    	//check if we need to correct the side
	    	if(f_tempSide < 0) {
	    		f_tempSide += 6; //+6 to get the correct side
	    	}
	    	
	    	//check if both side slots are empty
	    	if(m_neighbors[a_side] == null && a_node.m_neighbors[f_tempSide] == null)
	    	{
		    	//connect both node to each other
		    	m_neighbors[a_side] = a_node; //connect this node to the neighbor
		    	a_node.m_neighbors[f_tempSide] = this; //connect the neighbor to this node
		    	
		    	
		    	//check for common neighbors to connect to
		    	
		    	//right
		    	f_tempSide = a_side + 1;
		    	if(f_tempSide > 5) {
		    		f_tempSide -= 6;
		    	}
		    	//if there is a neighbor on the right side
		    	if(m_neighbors[f_tempSide] != null)
		    	{
		    		//if there is no previous node OR the neighbor is NOT the previous node
		    		if(a_previousNode == null || m_neighbors[f_tempSide] != a_previousNode)
		    		{
			    		//calc the neighbor's side to connect to
			        	int f_sideToConnect = a_side - 1;
			        	if(f_sideToConnect < 0) {
			        		f_sideToConnect += 6;
			        	}
			    		
			    		m_neighbors[f_tempSide].ConnectNeighbor(a_node, f_sideToConnect, this);
		    		}
		    	}
		    	else //if there is not a neighbor on the right side
		    	{
		    		//then we should check if there SHOULD be
		    		
		    		//get the center node's side to the possibly missing node
		    		int f_centerNodeSide = GetRightSide(f_tempSide);
		    		
		    		//check for the missing node
		    		if(a_node.m_neighbors[f_centerNodeSide] != null)
		    		{
		    			//we found it!
		    			//now connect them (twice!)
		    			
		    			System.out.println("Connect-order-bug found!");
		    			
		    			//circle the found node
		    			ConnectNeighbor(a_node.m_neighbors[f_centerNodeSide], f_tempSide, a_node);
		    			
		    			System.out.println("Node A:  " + this);
		    			System.out.println("Node B:  " + a_node.m_neighbors[f_centerNodeSide]);
		    			
		    			//set the missing neighbors connections back to null
		    			//NOTE: this is to be able to use ConnectNeighbor twice
		    			m_neighbors[f_tempSide] = null;
		    			a_node.m_neighbors[f_centerNodeSide].m_neighbors[GetOppositeSide(f_tempSide)] = null;
		    			
		    			System.out.println("Break connection");
		    			System.out.println("Node A:  " + this);
		    			System.out.println("Node B:  " + a_node.m_neighbors[f_centerNodeSide]);
		    			
		    			//connect them again (this time we circle the other node)
		    			a_node.m_neighbors[f_centerNodeSide].ConnectNeighbor(this, GetOppositeSide(f_tempSide), a_node);
		    			
		    			
		    			System.out.println("Fixed!");
		    			System.out.println("Node A:  " + this);
		    			System.out.println("Node B:  " + a_node.m_neighbors[f_centerNodeSide]);
		    		}
		    	}
		    	
		    		
		    	//left
		    	f_tempSide = a_side - 1;
		    	if(f_tempSide < 0) {
		    		f_tempSide += 6;
		    	}
		    	//if there is a neighbor on the left side
		    	if(m_neighbors[f_tempSide] != null)
		    	{
		    		//if there is no previous node OR the neighbor is NOT the previous node
		    		if(a_previousNode == null || m_neighbors[f_tempSide] != a_previousNode)
		    		{
			    		//calc the neighbor's side to connect to
			        	int f_sideToConnect = a_side + 1;
			        	if(f_sideToConnect > 5) {
			        		f_sideToConnect -= 6;
			        	}
			    		
			    		m_neighbors[f_tempSide].ConnectNeighbor(a_node, f_sideToConnect, this);
		    		}
		    	}
		    	else //if there is not a neighbor on the left side
		    	{
		    		//then we should check if there SHOULD be
		    		
		    		//get the center node's side to the possibly missing node
		    		int f_centerNodeSide = GetLeftSide(f_tempSide);
		    		
		    		//check for the missing node
		    		if(a_node.m_neighbors[f_centerNodeSide] != null)
		    		{
		    			//we found it!
		    			//now connect them (twice!)
		    			
		    			System.out.println("Connect-order-bug found!");
		    			
		    			//circle the found node
		    			ConnectNeighbor(a_node.m_neighbors[f_centerNodeSide], f_tempSide, a_node);
		    			
		    			System.out.println("Node A:  " + this);
		    			System.out.println("Node B:  " + a_node.m_neighbors[f_centerNodeSide]);
		    			
		    			//set the missing neighbors connections back to null
		    			//NOTE: this is to be able to use ConnectNeighbor twice
		    			m_neighbors[f_tempSide] = null;
		    			a_node.m_neighbors[f_centerNodeSide].m_neighbors[GetOppositeSide(f_tempSide)] = null;
		    			
		    			System.out.println("Break connection");
		    			System.out.println("Node A:  " + this);
		    			System.out.println("Node B:  " + a_node.m_neighbors[f_centerNodeSide]);
		    			
		    			//connect them again (this time we circle the other node)
		    			a_node.m_neighbors[f_centerNodeSide].ConnectNeighbor(this, GetOppositeSide(f_tempSide), a_node);
		    			
		    			
		    			System.out.println("Fixed!");
		    			System.out.println("Node A:  " + this);
		    			System.out.println("Node B:  " + a_node.m_neighbors[f_centerNodeSide]);
		    		}
		    	}
		    	
	    	}//END check if both side slots are empty
	    	else
	    	{
	    		// this should only occur in the case when adding a node that is surrounded by other nodes, 
	    		// otherwise it's a redundant function call (no biggie, but it shouldn't happen)
	    		System.out.println("Node.ConnectNeighbor WARNING: One of the sides is already taken, fix it! (if it's the special case then it's OK) ");
	    	}
    	}//check if the node this is connecting to is NULL
    	else
    	{
    		System.out.println("Node.ConnectNeighbor WARNING: The Node that this is trying to add is NULL");
    	}
    }
    
    
    
    public void ConnectPath(int a_side)
    {
    	if(m_neighbors[a_side] == null)
    	{
    		throw new RuntimeException("Node.ConnectPath ERROR: The neighbor to connect the path to is NULL");
    	}
    	
    	//this node is now a path
    	m_isPath = true;
    	//connect this node's side
    	m_connectedTo[a_side] = true;
    	
    	//now the other node
    	//this node is now a path
    	m_neighbors[a_side].m_isPath = true;
    	//connect this node's side (which is the opposite side)
    	m_neighbors[a_side].m_connectedTo[GetOppositeSide(a_side)] = true;
    }
    
    
    public Vector2 GetNeighborPos(int a_side)
    {
    	Vector2 f_result = new Vector2(m_pos.x, m_pos.y);
    	
    	switch(a_side)
    	{
    	case 0:
			//upper-right
    		f_result.x += xOffset * 0.5f;
    		f_result.y += -yOffset;
			break;
		case 1:
			//mid-right
			f_result.x += xOffset;
			break;
		case 2:
			//low-right
			f_result.x += xOffset * 0.5f;
			f_result.y += yOffset;
			break;
		case 3:
			//low-left
			f_result.x += -xOffset * 0.5f;
			f_result.y += yOffset;
			break;
		case 4:
			//mid-left
			f_result.x += -xOffset;
			break;
		case 5:
			//upper-left
			f_result.x += (float)(-xOffset * 0.5);
			f_result.y += -yOffset;
			break;
    	}
    	
    	return f_result;
    }
    
    
    public String toString()
    {
    	String f_result = "Node (" + m_id + ")";
    	
    	for(Node.Side s : Node.Side.values())
		{
    		if(m_neighbors[s.ordinal()] != null)
    		{
    			f_result += " " + s + "=" + m_neighbors[s.ordinal()].m_id;
    		}
    		else
    		{
    			f_result += " " + s + "=N";
    		}
		}
    	
    	return f_result;
    }
    
    public static int GetOppositeSide(int a_side)
    {
    	switch(a_side)
    	{
    	case 0: //top-right
    		return Side.BotLeft.ordinal();
		case 1: //mid-right
			return Side.MidLeft.ordinal();
		case 2: //bot-right
			return Side.TopLeft.ordinal();
		case 3: //bot-left
			return Side.TopRight.ordinal();
		case 4: //mid-left
			return Side.MidRight.ordinal();
		case 5: //top-left
			return Side.BotRight.ordinal();
		default:
			throw new RuntimeException("Node.GetOppositeSide ERROR: The side is NOT a side!");
    	}
    }
    
    public static int GetLeftSide(int a_side)
    {
    	switch(a_side)
    	{
    	case 0: //top-right
    		return Side.TopLeft.ordinal();
		case 1: //mid-right
			return Side.TopRight.ordinal();
		case 2: //bot-right
			return Side.MidRight.ordinal();
		case 3: //bot-left
			return Side.BotRight.ordinal();
		case 4: //mid-left
			return Side.BotLeft.ordinal();
		case 5: //top-left
			return Side.MidLeft.ordinal();
		default:
			throw new RuntimeException("Node.GetLeftSide ERROR: The side is NOT a side!");
    	}
    }
    
    public static int GetRightSide(int a_side)
    {
    	switch(a_side)
    	{
    	case 0: //top-right
    		return Side.MidRight.ordinal();
		case 1: //mid-right
			return Side.BotRight.ordinal();
		case 2: //bot-right
			return Side.BotLeft.ordinal();
		case 3: //bot-left
			return Side.MidLeft.ordinal();
		case 4: //mid-left
			return Side.TopLeft.ordinal();
		case 5: //top-left
			return Side.TopRight.ordinal();
		default:
			throw new RuntimeException("Node.GetRightSide ERROR: The side is NOT a side!");
    	}
    }
    
    public static Side GetSide(int a_side)
    {
    	switch(a_side)
    	{
    	case 0: //top-right
    		return Side.TopRight;
		case 1: //mid-right
			return Side.MidRight;
		case 2: //bot-right
			return Side.BotRight;
		case 3: //bot-left
			return Side.BotLeft;
		case 4: //mid-left
			return Side.MidLeft;
		case 5: //top-left
			return Side.TopLeft;
		default:
			throw new RuntimeException("Node.GetSide ERROR: The side is NOT a side!");
    	}
    }
    
    
    
}
